import base64
import enum
import os
import tkinter
from tkinter import ttk

import _icon
from NetEngine import NetEngine, ClientEvent
import NetProtocol.out.python.net_protocol as net_protocol
from _config import *
from _chat_window import ChatWindow


class TChatWindow:
    # 基本联网状态机
    class _NetStatus(enum.Enum):
        Init = 0
        """初始化引擎"""
        StartConnect = 1
        """开始连接平台"""
        WaitConnectResult = 2
        """等待连接结果"""
        Connected = 3
        """已连接"""

    # 链路状态机
    class _LinkStatus(enum.Enum):
        CheckCert = 0
        """检查证书"""
        RegisterPubKey = 1
        """注册公钥"""
        WaitRegisterPubKeyResult = 2
        """等待注册公钥结果"""
        Login = 3
        """登入"""
        WaitLoginResult = 4
        """等待登入结果"""
        Logined = 5
        """已登入"""
        Logout = 6
        """登出"""
        WaitLogoutResult = 7
        """等待登出结果"""
        Done = 8
        """完事"""

    def __init__(self):
        self._windows = tkinter.Tk()
        self._engine = NetEngine()
        self._protocol = net_protocol.NetProtocol()
        self.net_status: TChatWindow._NetStatus = TChatWindow._NetStatus.Init
        self.link_status: TChatWindow._LinkStatus = TChatWindow._LinkStatus.CheckCert
        self.uid = 0
        self.rx_buf = []
        width = 900
        height = 600
        screen_height = self._windows.winfo_screenheight()
        screen_width = self._windows.winfo_screenwidth()
        x = int(screen_width / 2 - width / 2)
        y = int(screen_height / 2 - height / 2)
        size = '{}x{}+{}+{}'.format(width, height, x, y)
        self._windows.geometry(size)
        self._windows.title("TChat(By.杨列兵)  " + version_)
        with open('tmp.ico', 'wb') as tmp:
            tmp.write(base64.b64decode(_icon.Icon().img))
        self._windows.iconbitmap('tmp.ico')
        os.remove('tmp.ico')

        # 左视图 标签页
        self.left_view = tkinter.Frame(self._windows)
        self.left_view.pack(side='left', fill='both', expand=True)
        self.notebook = ttk.Notebook(self.left_view, style="My.TNotebook")
        profile = tkinter.Frame(self._windows)
        tkinter.Label(profile, text='请于右方发起聊天').pack(expand=True, fill='both')
        self.notebook.add(profile, text='首页')
        self.notebook.pack(fill="both", expand=True)

        # 右视图 一坨东西，比如什么登录界面，在线列表啥的
        self.right_view = tkinter.Frame(self._windows, bg='green')
        self.right_view.pack(side='right', fill='y')

        tkinter.Label(self.right_view, text='在 线 列 表').pack(fill='x')
        self.line_search = tkinter.Frame(self.right_view)
        self.line_search.pack(fill='x')
        self.text_search = tkinter.Text(self.line_search, width=1, height=1)
        self.text_search.pack(side='left', fill='x', expand=True)
        self.button_search = tkinter.Button(self.line_search, text='搜索')
        self.button_search.pack(side='left')
        self.tree_online_list = ttk.Treeview(self.right_view, columns=(0, 1), show='headings')
        self.tree_online_list.heading(0, text='UID')
        self.tree_online_list.heading(1, text='用户名')
        self.tree_online_list.column(0, width=100, anchor='center')
        self.tree_online_list.column(1, width=100, anchor='center')
        self.tree_online_list.pack(fill='y', expand=True)
        self.line_username = tkinter.Frame(self.right_view)
        self.line_username.pack(fill='x')
        tkinter.Label(self.line_username, text='用户名:').pack(side='left')
        self.text_username = tkinter.Text(self.line_username, width=1, height=1)
        self.text_username.pack(side='left', fill='x', expand=True)
        self.button_login = tkinter.Button(self.right_view, text='登录')
        self.button_login.pack(fill='x')
        self.label_online_status = tkinter.Label(self.right_view, text='未连接平台')
        self.label_online_status.pack(fill='x')

        tkinter.Button(profile, text='text', command=self.add_tag).pack()

    def add_tag(self):
        profile = ChatWindow(self._windows, self._engine)
        self.notebook.add(profile, text='page')

    def _msg_recv(self, frame: bytes):
        print('Socket [recv]', frame.hex())
        msg = self._protocol.decode_msg(frame)
        if msg is not None:
            if type(msg) is net_protocol.MsgRegisterPublicKeyResult:
                print('Get Register PublicKey Result=%d' % msg.RegisterResult)
                if msg.RegisterResult == 0:  # 注册公钥成功，保存UID
                    self.uid = msg.NewUID
                    with open('~\\.tchat\\gw_pubkey.pem', mode='wb') as fp:
                        fp.write(bytes(msg.ServerPublicKey))
                    if not os.path.exists('~\\.tchat'):
                        os.makedirs('~\\.tchat')
                    with open('~\\.tchat\\uid', mode='w') as fp:
                        fp.write(str(self.uid))
                self.link_status = TChatWindow._LinkStatus.CheckCert
            elif type(msg) is net_protocol.MsgLoginResult:
                print('Get Login Result=%d' % msg.LoginResult)
                if msg.LoginResult == 0:  # 登入成功
                    self.link_status = TChatWindow._LinkStatus.Logined
                else:  # 登入失败
                    self.link_status = TChatWindow._LinkStatus.Login
        else:
            print('Decode failed')

    def _data_notify(self, is_recv: bool, data: bytes):
        if is_recv:
            self.rx_buf += list(data)
            frame = self._protocol.cut_frame(self.rx_buf)
            if frame is not None:
                self._msg_recv(frame)
        else:
            print('Socket [send]', data.hex())
        pass

    def _client_event(self, event: ClientEvent, remote_host: str, remote_port: int):
        print('client event:', event)
        if event == ClientEvent.Connected:
            if self.net_status == TChatWindow._NetStatus.WaitConnectResult:
                self.net_status = TChatWindow._NetStatus.Connected
                self.link_status = TChatWindow._LinkStatus.CheckCert
        else:
            if self.net_status == TChatWindow._NetStatus.Connected:
                self.net_status = TChatWindow._NetStatus.WaitConnectResult
                self.link_status = TChatWindow._LinkStatus.Done

    def _session_run(self):
        if self.link_status == TChatWindow._LinkStatus.CheckCert:
            print("""检查证书""")
            if os.path.exists('~\\.tchat\\gw_pubkey.pem') and os.path.exists(
                    '~\\.tchat\\pri_key.pem') and os.path.exists('~\\.tchat\\uid'):
                self._protocol.init_asymmetric('~\\.tchat\\gw_pubkey.pem', '~\\.tchat\\pri_key.pem')
                self.uid = int(open('~\\.tchat\\uid', mode='r').read())
                self.link_status = TChatWindow._LinkStatus.Login
            else:
                self.link_status = TChatWindow._LinkStatus.RegisterPubKey
        elif self.link_status == TChatWindow._LinkStatus.RegisterPubKey:
            print("""注册公钥""")
            # 生成密钥
            self._protocol.enc_module.generate_asymmetric_key()
            # 导出密钥
            pri_key = self._protocol.enc_module.export_asymmetric_local_private_key()
            if pri_key is not None:
                if not os.path.exists('~\\.tchat'):  # 如果父目录不存在，则创建它
                    os.makedirs('~\\.tchat')
                with open('~\\.tchat\\pri_key.pem', mode='wb') as fp:
                    fp.write(pri_key)
            else:
                print('导出密钥失败')
                return
            # 导出公钥
            pub_key = self._protocol.enc_module.export_asymmetric_local_public_key()
            # 注册公钥
            msg_register_key = net_protocol.MsgRegisterPublicKey(uid=0)
            msg_register_key.PublicKey = list(pub_key)
            msg_register_key.PublicKeyLen = len(msg_register_key.PublicKey)
            frame = self._protocol.encode_msg(msg_register_key)
            self._engine.send(frame)
            self.link_status = TChatWindow._LinkStatus.WaitRegisterPubKeyResult

        elif self.link_status == TChatWindow._LinkStatus.WaitRegisterPubKeyResult:
            """等待注册公钥结果"""
        elif self.link_status == TChatWindow._LinkStatus.Login:
            print("""登入""")
            login_msg = net_protocol.MsgLogin(self.uid)
            sy_key = os.urandom(16)
            self._protocol.init_symmetric(sy_key)
            login_msg.SymmetricKey = list(sy_key)
            login_msg.SymmetricKeyLen = len(login_msg.SymmetricKey)
            login_msg.SetEncryptMode(net_protocol.EncModeAsymmetric)
            frame = self._protocol.encode_msg(login_msg)
            self._engine.send(frame)
            self.link_status = TChatWindow._LinkStatus.WaitLoginResult
        elif self.link_status == TChatWindow._LinkStatus.WaitLoginResult:
            """等待登入结果"""
        elif self.link_status == TChatWindow._LinkStatus.Logined:
            """已登入"""
        elif self.link_status == TChatWindow._LinkStatus.Logout:
            print("""登出""")
        elif self.link_status == TChatWindow._LinkStatus.WaitLogoutResult:
            """等待登出结果"""
        elif self.link_status == TChatWindow._LinkStatus.Done:
            """完事"""

    def _run_once(self):
        if self.net_status == TChatWindow._NetStatus.Init:
            """初始化引擎"""
            self._engine.register_client_event_notify(self._client_event)
            self._engine.register_data_notify(self._data_notify)
            self._engine.set_param(auto_reconnect=True)
            self.net_status = TChatWindow._NetStatus.StartConnect
        elif self.net_status == TChatWindow._NetStatus.StartConnect:
            """开始连接平台"""
            self._engine.start_connect('localhost', 50200)
            self.net_status = TChatWindow._NetStatus.WaitConnectResult
        elif self.net_status == TChatWindow._NetStatus.WaitConnectResult:
            """等待连接结果"""
        elif self.net_status == TChatWindow._NetStatus.Connected:
            """已连接"""
            self._session_run()
        self._windows.after(10, self._run_once)

    def start(self):
        self._run_once()
        self._windows.mainloop()
